/*
The surface-physics project: http://code.google.com/p/surface-physics/
Adds real world physics based on the Bullet physics engine, to bases in the Orbiter space
flight simulation software.

Written by Abhijit Nandy
License : Apache License 2.0

Bump_ExternalStaticGeometry.cpp
Bump_ExternalStaticGeometry management implementation.


*/


/* Class declaration */
#include "Bump_ExternalStaticGeometry.h"


DerivedRegister<Bump_ExternalStaticGeometry> Bump_ExternalStaticGeometry::reg("Bump_ExternalStaticGeometry");


/**
 * Constructor for this derived class
 * Will be used if some attachment specific needs to be initialized comes up
 * Do NOT allocate memory here, let init() do that
 *
 * It is in fact currently used for a temporary vehicle simulation class which
 * should actually be derived from Bump_ExternalStaticGeometry.
 */
Bump_ExternalStaticGeometry::Bump_ExternalStaticGeometry()
{


}


/**
 * Destructor, does nothing currently
 */
Bump_ExternalStaticGeometry::~Bump_ExternalStaticGeometry()
{}


/**
 * Initialize a base component.
 * Creates a new BulletBase Attachment and inserts it in the Orbiter world & Bullet World
 *
 *
 */
int
Bump_ExternalStaticGeometry::init(Bump_BulletBase *bb, char *strObjName,  OBJHANDLE hObj)
{
	int rv;

	bumpBase = bb;
	strcpy(strObjectName, strObjName);

	sprintf(boutput, "Bump_ExternalStaticGeometry::init: Initializing %s", strObjectName);
	oapiWriteLog(boutput);

	//Create Orbiter object: Load any meshes by reading the mesh files from the physics file
	createOrbiterPrimitives();


	//Create physics counterpart, if this fails, should remove Orbiter meshes that were loaded above
	rv = createBulletRigidBody();
	if(rv){
		//Remove here if necess.

		sprintf(boutput, "Bump_ExternalStaticGeometry::init: ERROR : Creating rigid body failed %s",
				strObjectName);
		oapiWriteLog(boutput);
		return 1;
	}
	
	
	//Only after Orbiter AND Bullet successful, set physics to true
	physicsInited = true;




	return 0;
}

bool done = false;
/**
 * Updates a base attachment
 * Primarily move the vessel mesh into position according to latest transform by setting
 * attachment point parameter.
 * Needs to read and process any commands too from the containing base
 */
int
Bump_ExternalStaticGeometry::poststep(double SimT, double SimDT, double MJD)
{


	sprintf(boutput, "Bump_ExternalStaticGeometry::poststep: The next sim step will advance the sim by %f secs", SimDT);
	oapiWriteLog(boutput);

	(*(bumpBase->objIter))->freq = -1; //no updates needed

	return 0;
}


/**
 *	Process keyboard input any other messages, not used for this class, why send
 *	commands to terrain, unless we want to distort it in some way ? Damadge simulation ?
 *	Vehicle tracks ?
 */
int
Bump_ExternalStaticGeometry::processCommand()
{
/*	if(bbv->validKeyInput){

		//Command
		switch (bbv->keyInput.key)
		{
			case OAPI_KEY_UP:

				break;

			case OAPI_KEY_SPACE:
				break;



			case NO_KEY:

				break;

			default:
				break;
		}
	}
*/
	return 0;
}


/**
 * Deallocates any memory, Bump_ExternalStaticGeometry::exit() will be called
 * by the containing Bump_BulletBase.
 *
 * BUG: m_carChassis may not need to be deleted explicitly ?
 *
 * Delete any attachment points with the standard pattern, from BulletBase and Attached vessel
 */
int
Bump_ExternalStaticGeometry::exit()
{
	int rv;


	//Destroy physics counterpart
	rv = destroyBulletRigidBody();
	if(rv){
		sprintf(boutput, "Bump_ExternalStaticGeometry::exit: ERROR : Destroying rigid body failed for %s", strObjectName);
		oapiWriteLog(boutput);
	}


	//Remove Orbiter handles, update base
/*	rv = detachOrbiterVESSEL();
	if(rv){
		sprintf(boutput, "Bump_ExternalStaticGeometry::exit: ERROR : Detaching Orbiter VESSEL failed %s", strObjectName);
		oapiWriteLog(boutput);
	}
*/
	//Base class members cleanup
	Bump_BaseObject::exit();

	return rv;

}


/**
 * Only resets the physics body. There is no need to re-attach
 *
 * Currently complete vehicle memory de-allocation is disabled due to m_actions[] in the
 * btDiscreteDynamicsWorld not being properly resized m_actions.size() fails in an assertion.
 *
 * This has to be reproduced in the Vehicle demo and asked in the Bullet forums.
 * Also test with re-spawn(reqd for Orulex) & deallocate memory options in Orbiter debugging options.
 *
 *
 * Meanwhile reset() causes the vehicle to turn back upright if toppled.
 *
 * TODO: Causes crash, fix destroyBulletRigidBody(),
 * Test in VehicleDemo
 */
int
Bump_ExternalStaticGeometry::reset()
{




	return 0;
}



/**
 * Create any Orbiter stuff
 *
 */
int
Bump_ExternalStaticGeometry::createOrbiterPrimitives()
{

	return 0;
}


/**
 * Destroy the created Orbiter stuff
 *
 */
int
Bump_ExternalStaticGeometry::destroyOrbiterPrimitives()
{



	return 0;
}


/**
 * Create the physics counterpart in the Bullet world.
 *
 * Preferably get all info required from Orbiter and store in members beforehand.
 *
 * Optimize: reuse box shape by scaling a unit block for the pad sections and blocks
 *
 * TODO: Parse the base cfg file to get the shapes instead of hard coding ? Or merge
 * 		 base cfg with physics file for base ?
 *
 */
int
Bump_ExternalStaticGeometry::createBulletRigidBody()
{

	btTransform trans;
	btQuaternion q;


	//Create pads from box shapes
	unsigned long int pad;
	double equPadLng, equPadLat, equPadRad;
	VECTOR3 rposPad, gposPad;
	for(pad=0; pad<oapiGetBasePadCount(bumpBase->hObjBase); pad++){
		oapiGetBasePadEquPos(bumpBase->hObjBase, pad, &equPadLng, &equPadLat, &equPadRad);

		/*oapiEquToLocal(oapiGetBaseByName(oapiGetObjectByName("Moon"), "Brighton Beach"),
			equPadLng, equPadLat, equPadRad, &rposPad);*/

		oapiEquToGlobal(oapiGetObjectByName("Moon"), equPadLng, equPadLat, equPadRad, &gposPad);

		bumpBase->bb->Global2Local(gposPad, rposPad);

		sprintf(boutput, "Bump_ExternalStaticGeometry::createBulletRigidBody:" \
				"Pad %d: Long:%f, Lat:%f, Rad:%f >> (%f,%f,%f)",
				pad,
				equPadLng, equPadLat, equPadRad,
				rposPad.x, rposPad.y, rposPad.z);
		oapiWriteLog(boutput);

		createBulletPad( btScalar(rposPad.x),  btScalar(rposPad.y),  btScalar(rposPad.z));
	}


	//Create Tank, block and train geometry

	//Here is the tank
	trans.setIdentity();
	trans.setOrigin(btVector3(0.f,+50.f,0.f));
	q.setEuler(0.f, 0.f,  btScalar(PI/2));
	trans.setRotation(q);
	btCollisionShape* cylshpTank =
			new btCylinderShapeX(btVector3(50.f, 25.f, 25.f)); //y ht :half extent, rest are radii

	bumpBase->m_collisionShapes->push_back(cylshpTank);
	bumpBase->localCreateRigidBody(0.0f, trans, cylshpTank);

	//Now the tank top block
	btCollisionShape* boxshpBlock1 = new btBoxShape(btVector3(1.f, 1.f, 1.f));
	bumpBase->m_collisionShapes->push_back(boxshpBlock1);

	trans.setIdentity();
	trans.setOrigin(btVector3(0.f, 100.f + 2.5f, 5.f));
	q.setEuler(0.f, 0.f, 0.f);
	trans.setRotation(q);
	boxshpBlock1->setLocalScaling(btVector3(10.f, 2.5f, 10.f));	
	bumpBase->localCreateRigidBody(0.0f, trans, boxshpBlock1);

	//Now the tank side block
	trans.setIdentity();
	trans.setOrigin(btVector3(20.f, 0.f + 10.f, -40.f));
	q.setEuler(0.f, 0.f, 0.f);
	trans.setRotation(q);
	btCollisionShape* boxshpBlock2 = new btBoxShape(btVector3(1.f, 1.f, 1.f));
	bumpBase->m_collisionShapes->push_back(boxshpBlock2);
	boxshpBlock2->setLocalScaling(btVector3(20.f, 10.f, 40.f));
	bumpBase->localCreateRigidBody(0.0f, trans, boxshpBlock2);



	//No train yet!


	


	return 0;
}


/**
 * Creates a pad at the position passed relative to base
 */
int
Bump_ExternalStaticGeometry::createBulletPad(btScalar rposX,
											 btScalar rposY,
											 btScalar rposZ)
{
	btTransform transPadSection;


	btQuaternion q;

	btScalar r = 40.3f, x = 0.f, z = 0.f, cubeHalfLength = 0.5;
	btScalar turnAngle = 45.0f;
	float i;

	btCollisionShape* boxshpPadSection = new btBoxShape(btVector3(0.2f,
												0.2f,
											16.0f));
	bumpBase->m_collisionShapes->push_back(boxshpPadSection);

	for(i=0; i<360; i+=turnAngle){

		x = r * cos(i* btScalar(PI)/180.f);
		z = r * sin(i* btScalar(PI)/180.f);

		q.setEuler(-i*(btScalar(PI/180.f)),  0, 0);

		transPadSection.setIdentity();
		transPadSection.setRotation(q);
		transPadSection.setOrigin(btVector3(x + rposX, 0.2f + rposY, z + rposZ));

		bumpBase->localCreateRigidBody(0.0f, transPadSection, boxshpPadSection);
	}


	return 0;
}


/**
 * De-allocates memory being consumed by the raycast vehicle so it can be
 * created again by createBulletRigidBody()
 *
 * This is a very important function. If memory is not freed up, Orbiter sim will not
 * exit properly and the Launchpad will not appear leading to user frustration!
 *
 * Here we de-allocate only the sub class specific stuff which have not been freed by
 * bump.clientResetScene(), which is called before Bump_BulletBase::exit();
 *
 * Thus rigid bodies(derived from CollisionObjects), motion states are already removed
 * by the time we come here, DO NOT remove them again !
 *
 * The stuff which should be removed are any sub class specific classes used to model
 * this object like m_vehicleRayCaster & m_vehicle. Also any collision shapes not pushed
 * into m_collisionShapes[] during init(), needs to be freed here, such as the wheelShape
 */
int
Bump_ExternalStaticGeometry::destroyBulletRigidBody()
{
	physicsInited = false;



	return 0;
}
